package Primitives;

import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Memorizer<A, V> implements Computable<A, V> {
	private final Computable<A, V> computer;
	private final ConcurrentMap<A, Future<V>> cashe = new ConcurrentHashMap<A, Future<V>>();

	public Memorizer(Computable<A, V> computer) {
		this.computer = computer;
	}

	@Override
	public V compute(final A arg) throws InterruptedException {
		while (true) {
			Future<V> future = cashe.get(arg);
			if (future == null) {
				Callable<V> eval = new Callable<V>() {
					public V call() throws Exception {
						return computer.compute(arg);
					}
				};
				FutureTask<V> ft = new FutureTask<V>(eval);
				future = cashe.putIfAbsent(arg, ft);
				if (future == null) {
					future = ft;
					ft.run();
				}
			}
			try {
				return future.get();
			} catch (CancellationException e) {
				cashe.remove(arg);
			} catch (ExecutionException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

class ExpensiveComputations implements Computable<String, BigInteger> {
	
	@Override
	public BigInteger compute(String arg) throws InterruptedException {
		
		return new BigInteger(arg);
	}
	
}
